﻿using System.Linq.Expressions;
using System.Reflection;

namespace Quartz.MUI.ClassLibrary.Model
{
    public static class ExpressionExtensions
    {

        public static PropertyInfo GetPropertyInfo<TSource>(this Expression<Func<TSource, object>> expression)
        {
            var member = expression.Body as MemberExpression;
            if (member != null)
            {
                return member.Member as PropertyInfo;
            }
            return null;
        }

        private static Expression<T> Combine<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            var visitor = new ReplaceExpressionVisitor(first.Parameters[0]);
            var body1 = visitor.Visit(first.Body);
            var body2 = visitor.Visit(second.Body);
            return Expression.Lambda<T>(merge(body1, body2), first.Parameters[0]);
        }

        public static Expression<Func<TIn, bool>> Append<TIn>(this Expression<Func<TIn, bool>> expression, Expression<Func<TIn, bool>> append, Func<Expression, Expression, Expression> mergeType, bool cond = true)
        {
            if (cond)
            {
                return expression.Combine(append, mergeType);
            }
            return expression;
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second, bool cond = true)
        {
            return first.Append(second, Expression.AndAlso, cond);
        }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second, bool cond = true)
        {
            return first.Append(second, Expression.Or, cond);
        }

        private static Expression<Func<T, bool>> Merge<T>(this IEnumerable<Expression<Func<T, bool>>> expressions, Func<Expression, Expression, Expression> merge)
        {
            if (expressions?.Any() ?? false)
            {
                var first = expressions.First();
                var others = expressions.Skip(1);
                foreach (var item in others)
                {
                    first = first.Combine(item, merge);
                }
                return first;
            }
            return null;
        }

        public static Expression<Func<T, bool>> MergeAnd<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
        {
            return expressions.Merge(Expression.AndAlso);
        }

        public static Expression<Func<T, bool>> MergeOr<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
        {
            return expressions.Merge(Expression.Or);
        }
    }

    public class ReplaceExpressionVisitor : ExpressionVisitor
    {
        public ParameterExpression _Parameter { get; set; }

        public ReplaceExpressionVisitor(ParameterExpression Parameter)
        {
            _Parameter = Parameter;
        }
        protected override Expression VisitParameter(ParameterExpression p)
        {
            return _Parameter;
        }

        public override Expression Visit(Expression node)
        {
            return base.Visit(node);
        }
    }
}
